#ifndef HARD_CODDED_DATA_INPUT_BOX_H_JM_20120626
#define HARD_CODDED_DATA_INPUT_BOX_H_JM_20120626
/// @file HardCodedDataInputBox.h
/// _OK_ Vstupny box poskytujuci data, ktore su zadane priamo v kode.

// ##
#include "InputBox.h"
// ## C++
#include <map>

namespace jm4bbx {

	/**
	* Box zabezpecujuci vstup dat do modelu vypoctu, ktory vsak nennacita data 
	* z externeho zdroja ale ma data v sebe napevno zadane. 
	* HardCodedDataInputBox respektuje zdedeny memeber 
	* {@link inputSpecification_}, podla ktoreho zpristupnuje rozne datasety.
	* Vazba hodnoty z inputSpecification_ a konkretneho datasetu je opat 
	* vytvorena pramo v kode.
	* 
	* Datasety je mozne do HardCodedDataInputBox dynamicky pridavat pomocou 
	* {@link registerDatasource(const std::string&, DatasetTypePtr)} a tiez 
	* pripadne automatizovat pri spusteni programu pomocou triedy 
	* {@link DatasetRegistrator}.
	* 
	* @tparam DatasetType typ datasetov - struktur, ktore fyzicky obsahuju 
	* poskytovane data.
	*/
	template<class DatasetType>
	class HardCodedDataInputBox : public InputBox  {
	public:
		/// Typ politiky typu spravovanych dat.
		typedef typename DatasetType::policyType policyType;
		/// Pointer na dataset nesuci data.
		typedef std::shared_ptr<DatasetType> DatasetTypePtr;
		/// Typ mapy pre uchovanie vsetkych datasetov podla ich specifikacii.
		typedef std::map<std::string, DatasetTypePtr> DatasetMap;

		/**
		 * DatasetRegistrator sluzi na automaticke zaregistrovanie vazby 
		 * specifikacie a datasetu do triedy HardCodedDataInputBox (resp. 
		 * jeho mapy datasetov) pri spusteni programu.
		 * Registracia prebehne jednoducho konstrukciou objektu 
		 * DatasetRegistrator. 
		 **/
		struct DatasetRegistrator {
			/// @param spec specifikacia datasetu
			/// @param ds dataset nesuci data
			/// @see HardCodedDataInputBox::registerDatasource(const std::string&, DatasetTypePtr)}
			DatasetRegistrator(const std::string& spec, DatasetTypePtr ds) {
				HardCodedDataInputBox::registerDatasource(spec, ds);
			}
		};

		HardCodedDataInputBox(const BoxParamPack& p) : InputBox(p) { }

		/// Registracia datasetu do mapy datasetov poskytovanych boxom.
		/// 
		/// @warning Specifikacia musi byt unikatna v ramci vsetkych boxov 
		/// rovnakeho typu - mapa je staticky member.
		/// 
		/// @param specification unikatna specifikacia datasetu
		/// @param ds dataset nesuci data
		static void registerDatasource(const std::string& specification, 
			DatasetTypePtr ds) {

			if (datasetMap_ == nullptr) {
				datasetMap_ = new DatasetMap;
			}
			(*datasetMap_)[specification] = ds;
		}


		virtual size_t getColumnSize() const override {
			return getDataset()->getRowCount();
		}

	protected:

		virtual void loadData() override {
			const size_t columns = getDataset()->getColumnCount();
			const size_t rows = getDataset()->getRowCount();		

			for (size_t col = 0; col < columns; ++col) {					
				for (size_t row = 0; row < rows; ++row) {
					get_data<policyType>(columnType(col))[row] = 
						getDataset()->getValue(col, row);
				}	
			}
		};

		/// Vrati dataset aktivny podla specifikacie.
		DatasetTypePtr getDataset() {
			return (*datasetMap_)[inputSpecification_];
		}

		/// Vrati dataset aktivny podla specifikacie.
		const DatasetTypePtr getDataset() const {
			return (*datasetMap_)[inputSpecification_];
		}
	private:
		/// Mapa datasetov urcujuca vazbu specifikacia-dataset.
		/// @warning POZOR STATICKY MEMEBER!!!
		/// Mapa je staticka kvoli automatickej registracii datasetov + setrenie 
		/// pamate nakolko sa jedna o konstantne data.
		static DatasetMap* datasetMap_;
	};

	template<class DatasetType>
	typename HardCodedDataInputBox<DatasetType>::DatasetMap* 
		HardCodedDataInputBox<DatasetType>::datasetMap_;
	
} // namespace jm4bbx

#endif
